package com.luoyx.hauyne.security.autoconfigure;

import com.luoyx.hauyne.security.context.UserContextHolder;
import com.luoyx.hauyne.security.pojo.CurrentSysUser;
import com.luoyx.hauyne.security.pojo.Menu;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
 * 自定义主体信息提取器
 * <p>
 * 兼容OAuth2的客户端模式（客户端模式没有用户参与，只有客户端信息）
 *
 * @author Alan.Luo
 */
@Slf4j
public class OAuth2PrincipalExtractor implements PrincipalExtractor {
    @Override
    public Object extractPrincipal(Map<String, Object> map) {
        Object clientOnly = map.get("clientOnly");
        if (Boolean.TRUE.equals(clientOnly)) {
            Object name = map.get("name");
            log.info("当前是【OAuth2客户端模式】，客户端名称 => {}", name);

            return name;
        }
//        // 获取OAuth2Authentication对象
//        OAuth2Authentication authentication = (OAuth2Authentication) map;
//        if (authentication != null && authentication.isClientOnly()) {
//            // 如果是客户端模式，返回特定的标识，例如"client"字符串
//            return "client";
//        }

        // Map对应授权服务器的UserDetails.(在此Demo中，UserDetails的实现为CurrentSysUser,Map中必然包含该类的字段，挨个取出，构建实例。)
        Collection<? extends GrantedAuthority> authorities = getAuthorities(map);

        /**
         * 对于 Map<String, Object>由于我们使用Object类型来接收数字，map并不知道我们传入的是Long还是Integer。所以Map会根据我们传入的数值的大小来判断。
         *
         * 也就是说如果你传入的数值的大小是在 Integer.MIN_VALUE、 Integer.MAX_VALUE 之间，Map 就认为是 Integer 类型，如果超过了这个范围就认为是 Long 类型。
         */
        final Long id;
        Object value = map.get("id");
        if (value instanceof Integer) {
            id = ((Integer) value).longValue();
        } else {
            id = (Long) value;
        }
        UserContextHolder.setUserId(id);

        boolean enabled = map.get("enabled") != null && (boolean) map.get("enabled");
        boolean accountNonExpired = map.get("accountNonExpired") != null && (boolean) map.get("accountNonExpired");
        boolean credentialsNonExpired = map.get("credentialsNonExpired") != null && (boolean) map.get("credentialsNonExpired");
        boolean accountNonLocked = map.get("accountNonLocked") != null && (boolean) map.get("accountNonLocked");
        String nickname = (String) map.get("nickname");
        String realName = (String) map.get("realName");
        String avatar = (String) map.get("avatar");
        String phone = (String) map.get("phone");
        String email = (String) map.get("email");
        String position = (String) map.get("position");
        String department = (String) map.get("department");
        List<Menu> menus = (List<Menu>)map.get("menus");


        CurrentSysUser currentSysUser = new CurrentSysUser(
                (String) map.get("username"),
                "N/A",
                enabled,
                accountNonExpired,
                credentialsNonExpired,
                accountNonLocked,
                new HashSet<>(authorities),
                id,
                nickname,
                realName,
                avatar,
                phone,
                email,
                position,
                department,
                menus
        );

        return currentSysUser;
    }

    private static Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
        Object authorities = map.get(UserAuthenticationConverter.AUTHORITIES);
        if (authorities instanceof String) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
        }
        if (authorities instanceof Collection) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
                    .collectionToCommaDelimitedString((Collection<?>) authorities));
        }
        throw new IllegalArgumentException("Authorities must be either a String or a Collection");
    }
}
